
/**
\file Definition of nmn::math::quaternion.
\author Sean Tromans
*/

#include <needs_more_ninja/template/math/quaternion/quaternion.hpp>

namespace nmn
{

	namespace math
	{

		/**
		Default constructor. Does nothing.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline quaternion< TValueType >::quaternion( )
		{
		}

		/**
		Copy constructr from another quaternion.
		@param rhs The quaternion to copy.
		\author Sean Tromans
		*/
		template < typename TValueType >
		template < typename TOtherValueType >
		inline quaternion< TValueType >::quaternion( quaternion< TOtherValueType > const & rhs )
			:	m_vector( rhs.m_vector )
		{
		}

		/**
		Construct a quaternion from the four components
		@param x The x component.
		@param y The y component.
		@param z The z component.
		@param w The w component.
		\author Sean Tromans
		*/
		template < typename TValueType >
		template < typename T0, typename T1, typename T2, typename T3 >
		inline quaternion< TValueType >::quaternion( T0 const & x, T1 const & y, T2 const & z, T3 const & w )
			:	m_vector( x, y, z, w )
		{
		}

		/**
		Assign this quaternion to be equal to another.
		@param rhs The quaternion to copy.
		\author Sean Tromans
		*/
		template < typename TValueType >
		template < typename TOtherValueType >
		inline typename quaternion< TValueType >::my_type & quaternion< TValueType >::operator = ( quaternion< TOtherValueType > const & rhs )
		{
			m_vector = rhs.m_vector;
			return *this;
		}

		/**
		The size of the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::size_type quaternion< TValueType >::size( ) const
		{
			return m_vector.size( );
		}

		/**
		Component wise addition of another quaternion onto this.
		@param rhs The quaternion to be added.
		@return A reference to this after the addition.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::my_type & quaternion< TValueType >::operator += ( typename quaternion< TValueType >::my_type const & rhs )
		{
			m_vector += rhs.m_vector;
			return *this;
		}

		/**
		Component wise subtraction of another quaternion onto this.
		@param rhs The quaternion to be subtracted.
		@return A reference to this after the subtraction.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::my_type & quaternion< TValueType >::operator -= ( typename quaternion< TValueType >::my_type const & rhs )
		{
			m_vector -= rhs.m_vector;
			return *this;
		}

		/**
		Component wise multiplication of another quaternion onto this.
		@param rhs The quaternion to be multiplied.
		@return A reference to this after the multiplication.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::my_type & quaternion< TValueType >::operator *= ( typename quaternion< TValueType >::my_type const & rhs )
		{
			m_vector *= rhs.m_vector;
			return *this;
		}

		/**
		Component wise multiplication of a value type onto this.
		@param rhs The value type to be multiplied.
		@return A reference to this after the multiplication.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::my_type & quaternion< TValueType >::operator *= ( typename quaternion< TValueType >::value_type const & rhs )
		{
			m_vector *= rhs;
			return *this;
		}

		/**
		Component wise division of a value type onto this.
		@param rhs The value type to be divided.
		@return A reference to this after the division.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::my_type & quaternion< TValueType >::operator /= ( typename quaternion< TValueType >::value_type const & rhs )
		{
			m_vector /= rhs;
			return *this;
		}

		/**
		Component wise strict equality check against another quaternion.
		@param rhs The quaternion to compare against.
		@return True if each corresponding component in the two quaternion are exactly equal.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline bool quaternion< TValueType >::strict_equals( typename quaternion< TValueType >::my_type const & rhs ) const
		{
			return m_vector.strict_equals( rhs.m_vector );
		}

		/**
		Gets a component from inside the quaternion.
		@param index The index of the component to return.
		@return a reference to the component at \c index.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::reference_type quaternion< TValueType >::operator[] ( typename quaternion< TValueType >::size_type index )
		{
			return at( i );
		}

		/**
		Gets a component from inside the quaternion.
		@param index The index of the component to return.
		@return a constant reference to the component at \c index.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::const_reference_type quaternion< TValueType >::operator[] ( typename quaternion< TValueType >::size_type index ) const
		{
			return at( i );
		}

		/**
		Gets a component from inside the quaternion.
		@param index The index of the component to return.
		@return a reference to the component at \c index.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::reference_type quaternion< TValueType >::at( typename quaternion< TValueType >::size_type index )
		{
			return m_vector.at( index );
		}

		/**
		Gets a component from inside the quaternion.
		@param index The index of the component to return.
		@return a constant reference to the component at \c index.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::const_reference_type quaternion< TValueType >::at( typename quaternion< TValueType >::size_type index ) const
		{
			return m_vector.at( index );
		}

		/**
		Returns an iterator to the first element in the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::iterator quaternion< TValueType >::begin( )
		{
			return m_vector.begin( );
		}

		/**
		Returns a delimiting iterator to the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::iterator quaternion< TValueType >::end( )
		{
			return m_vector.end( );
		}

		/**
		Returns a constant iterator to the first element in the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::const_iterator quaternion< TValueType >::begin( ) const
		{
			return cbegin( );
		}

		/**
		Returns a delimiting constant iterator to the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::const_iterator quaternion< TValueType >::end( ) const
		{
			return cend( );
		}

		/**
		Returns a constant iterator to the first element in the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::const_iterator quaternion< TValueType >::cbegin( ) const
		{
			return m_vector.cbegin( );
		}

		/**
		Returns a delimiting constant iterator to the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::const_iterator quaternion< TValueType >::cend( ) const
		{
			return m_vector.cend( );
		}

		/**
		Returns a reverse iterator to the last element in the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::reverse_iterator quaternion< TValueType >::rbegin( )
		{
			return m_vector.rbegin( );
		}

		/**
		Returns a delimiting reverse iterator to the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::reverse_iterator quaternion< TValueType >::rend( )
		{
			return m_vector.rend( );
		}

		/**
		Returns a constant reverse iterator to the last element in the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::const_reverse_iterator quaternion< TValueType >::rbegin( ) const
		{
			return m_vector.rbegin( )
		}

		/**
		Returns a delimiting constant reverse iterator to the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::const_reverse_iterator quaternion< TValueType >::rend( ) const
		{
			return m_vector.rend( );
		}

		/**
		Swaps this quaternion with another.
		@param rhs The quaternion to swap with.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline void quaternion< TValueType >::swap( typename quaternion< TValueType >::my_type & rhs )
		{
			m_vector.swap( rhs.m_vector );
		}

		/**
		Retreives the raw data contained within the quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline typename quaternion< TValueType >::value_type const * quaternion< TValueType >::data( ) const
		{
			return m_vector.data( );
		}

		/**
		\relates nmn::math::quaternion
		Outputs a vector2 to an output stream.
		@param os The output ostream.
		@param vec The vector to output.
		@return A reference to the output stream.
		\author Sean Tromans
		*/
		template < typename Elem, typename Traits, typename TValueType >
		inline std::basic_ostream< Elem, Traits > & operator << ( std::basic_ostream< Elem, Traits > & os, quaternion< TValueType > const & q )
		{
			os << "{ ";
			std::copy(	q.begin(), q.end(), std::ostream_iterator< TValueType >( os, " " ) );
			return os << "}";
		}

		/**
		\relates nmn::math::quaternion
		Component wise absolute equality check.
		@param lhs The first quaternion to compare.
		@param rhs The second quaternion to compare.
		@return true if the two quaternion are exactly equal.
		\author Sean Tromans
		*/
		template < typename TValueType >
		inline bool operator == ( quaternion< TValueType > const & lhs, quaternion< TValueType > const & rhs )
		{
			return lhs.strict_equals( rhs );
		}

	};

};

namespace std
{
	/**
	\relates nmn::math::quaternion
	Swaps the values of two quaternion.
	@param lhs The first quaternion.
	@param rhs The second quaternion.
	\author Sean Tromans
	*/
	template < typename TValueType >
	inline void swap( nmn::math::quaternion< TValueType > & lhs, nmn::math::quaternion< TValueType > & rhs )
	{
		lhs.swap( rhs );
	}
}
